package com.rainszj.config;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author rainszj
 * 2020/3/10
 */
@Configuration
public class ShiroConfig {

    /**
     * ShiroFilterFactoryBean:3
     *
     * @param defaultWebSecurityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {

        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);


        /**
         *  anon：无需认证就可以访问
         *  authc：必须认证了才能访问
         *  user： 拥有记住我或认证功能才能使用
         *  perms：拥有对某个资源的权限才能访问
         *  role：拥有某个角色权限才能访问
         *
         *  配置访问权限 必须是LinkedHashMap，因为它必须保证有序
         *  过滤链定义，从上向下顺序执行，一般将 /**放在最为下边 --> : 这是一个坑，一不小心代码就不好使了
         *  其他资源都需要认证  authc 表示需要认证才能进行访问 user表示配置记住我或认证通过可以访问的地址
         */
        // 添加shior内置的过滤器，和 Druid 一样，往 Map里put值
        Map<String, String> filterMap = new LinkedHashMap<>();

        // 拦截请求路径
        filterMap.put("/admin/**", "user");

        // 使用记住我功能必须使用 user，虽然remember的cookie存下来，但无法访问后台
        // filterMap.put("/admin/*", "authc");

        bean.setFilterChainDefinitionMap(filterMap);

        // 登录拦截路径
        bean.setLoginUrl("/login");
        // 登录成功之后跳转的路径
        bean.setSuccessUrl("/admin/index");
        // 设置未授权跳转的路径
        bean.setUnauthorizedUrl("/login");

        return bean;
    }


    /**
     * DefaultWebSecuriyManager:2
     *
     * @param userRealm
     * @return
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        // 关联 Realm
        securityManager.setRealm(userRealm);
        // 设置记住我功能
        securityManager.setRememberMeManager(cookieRememberMeManager());

        return securityManager;
    }


    /**
     * 创建 Realm 对象，需要自定义:1
     *
     * @return
     */
    @Bean
    public UserRealm userRealm(@Qualifier("credentialsMatcher") HashedCredentialsMatcher matcher) {
        UserRealm userRealm = new UserRealm();

        // 关闭授权缓存
        // userRealm.setAuthorizationCachingEnabled(false);
        // 设置凭证匹配器
        userRealm.setCredentialsMatcher(matcher);

        return userRealm;
    }


    /**
     * 密码校验规则 HashedCredentialsMatcher
     * 这个类是为了对密码进行编码的 ,
     * 防止密码在数据库里明码保存 , 当然在登陆认证的时候 ,
     * 这个类也负责对form里输入的密码进行编码
     * 处理认证匹配处理器：如果自定义需要实现继承HashedCredentialsMatcher
     */
    @Bean(name = "credentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 设置加密的算法名
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
        // 设置加密的迭代次数
        hashedCredentialsMatcher.setHashIterations(1023);
        // 开启16进制存储证书（密码）
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);

        return hashedCredentialsMatcher;
    }


    /**
     * FormAuthenticationFilter 过滤器 过滤记住我
     *
     * @return
     */
/*    @Bean
    public FormAuthenticationFilter formAuthenticationFilter() {
        FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
        //对应前端的checkbox的name = rememberMe
        formAuthenticationFilter.setRememberMeParam("rememberMe");
        return formAuthenticationFilter;
    }*/


    /**
     * Cookie记住我管理对象，用来管理多个cookie对象
     */
    @Bean
    public CookieRememberMeManager cookieRememberMeManager() {

        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        // 设置记住我cookie
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }

    /**
     * Cookie对象
     *
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        //System.out.println("ShiroConfiguration.rememberMeCookie()");
        //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        // 设置cookie的存活时间，以秒为单位，采用GMT(英国格林尼治时间，比北京时间慢8个小时)，设置保存2周
        cookie.setMaxAge(1209600);
        cookie.setPath("/");
        //设为true后，只能通过http访问，javascript无法访问
        //防止xss读取cookie
        cookie.setHttpOnly(true);
        return cookie;
    }


}